#ifndef _sdl_vectormap_h_
#define _sdl_vectormap_h_

#include "sdl_mvector.h"
#include "sdl_debug.h"
#include "sdl_prim.h"
#include "sdl_ptr.h"
#include <vector>
#include <stdexcept>
#include <iostream>

/**
 * Mapa m wektorów n-wymiarowych typu data_t na bufor typu data_t.
 *
 * Możliwa zabawa zakresami i wieloma mapami tego samego zbioru. Pomysł jest taki, by dane w miarę możliwości umieszczać w dużym zbiorze, który potem idzie do karty grafizcznej, a w międzyczasie pokryć go sobie mapami i tymi mapami wprowadzać w nim zmiany, tak jakby to był zbiór wektorów.
 *
 * Uwaga: problem może pojawić się, gdy ktoś zechce uwolnić bufor zanim zrobi to mapa nadrzędna.
 */
template <class data_t, class vector_t, int n> //tak to musi być zdefiniowane, bo mvector3<float> i mvector<float,3> to nie to samo

class vectormap : public sdl_prim {
    template<class _d, class _v, int _n>

    friend class vbomap;

protected:
    ///właściwe mapowane dane
    data_t * data;
    ///wektory mapujące
    std::vector<vector_t> vectors;
    ///liczba wektorów w mapie
    int m;
    ///false jeżeli dostaliśmy dane z zewnątrz i nie będziemy ich kasować
    bool locked;
    ///przydatne jeżeli wzięliśmy zakres innej mapy
    int start, end;
public:

    /**
     * \brief Konstruktor tworzący zbiór i mapowanie
     * @param _m liczba wektorów do utworzenia na starcie
     * @param add ile jeszcze miejsca zarezerwować
     */
    vectormap(int _m, const std::string & title = "vectormap", int add = 0) :
    m(_m),
    locked(true),
    sdl_prim(std::string("vectormap from scratch: ") + title) {
        _msg("vectormap", 4, "tworzenie nowej mapy wektorowej z " << m << " elementów: " << name());

        if (_m == 0) return;

        vectors.reserve(m + add);

        data = new data_t[(m + add) * n];

        for (int i = 0; i < n * (m + add); i++)
            data[i] = 0.0;

        for (int i = 0; i < m; i++)
            vectors.push_back(vector_t(data, i));

        start = 0;
        end = m;
    }

    /**
     * \brief Mapowanie zakresu innej mapy [start,end)
     * @param map mapa źródłowa
     * @param _start początek zakresu
     * @param _end koniec zakresu
     */
    vectormap(const vectormap & map, int _start, int _end, const std::string &title = "vectormap") :
    locked(false),
    sdl_prim(std::string("vectormap from range: ") + title) {
        m = _end - _start + 1;

        try {
            data = const_cast<data_t*> (map.range(_start, _end));
        } catch (std::range_error e) {
            throw (e);
        }

        vectors.reserve(m);

        for (int i = 0; i < m; i++)
            vectors.push_back(vector_t(data, i));
        start = _start;
        end = _end;
    }

    vectormap(const vectormap & map) :
    sdl_prim(map.name()),
    data(map.data),
    vectors(map.vectors),
    m(map.m),
    locked(false),
    start(map.start),
    end(map.end) {
    }

    virtual ~vectormap() {
        if (locked) {
            _msg("vectormap", 3, "kasowanie mapy wektorowej o " << m << " elementów: " << name());
            delete [] data;
            vectors.clear();
        }
    }

    void operator=(const vectormap & map) {
        __name = map.name();
        data = map.data;
        vectors = map.vectors;
        m = map.m;
        locked = false;
        start = map.start;
        end = map.end;
    }

    ///daj mapę, która jest zakresem tej mapy [start,end)

    vectormap operator()(int _start, int _end) {
        return vectormap(*this, _start, _end);
    }

    ///daj wektor

    vector_t & operator[](int k) {
        if (k < m)
            return vectors[k];
        else throw (std::range_error(std::string("vectormap: index out of range")));
    }

    ///ustawienie danych

    void set_data(const data_t * d) {
        if (locked)
            delete [] data;

        data = d;

        locked = false;
    }

    ///pojemność mapy

    const int capacity() {
        return vectors.capacity();
    }

    ///włóż kolejnych k wektorów

    void insert(const int k = 1) {
        if (capacity() >= (m + k)) {
            for (int i = 0; i < k; i++)
                vectors.push_back(vector_t(data, m + i));

            m += k;
        } else throw (std::out_of_range("vectormap: capacity too low"));
    }

    operator data_t*() {
        return data;
    }

    ///daj liczbę wektorów

    const int size() const {
        return m;
    }

    ///daj liczbę bajtów

    const int size_bytes() const {
        return m * n * sizeof (data_t);
    }

    const int get_start() const {
        return start;
    }

    const int get_end() const {
        return end;
    }

    ///daj wskaźnik do zakresu bufora

    const data_t * range(int _start, int _end) const {
        data_t * r = data;
        r += _start*n;

        if (end < m)
            return r;
        else throw (std::range_error("vectormap: end beyond range"));

        return r;
    }
};

/*
 * przydatne typedefy
 */
typedef vectormap<float, mvector3f, 3 > mvector3fmap;
typedef vectormap<float, mvector4f, 4 > mvector4fmap;
typedef vectormap<float, mvector2f, 2 > mvector2fmap;

#endif //_sdl_vectormap_h_

